﻿using System;
using System.Collections.Generic;
using Bling.Util;
using Bling.DSL;
using Bling.Core;
using Bling.Graphics;
using System.Text;

namespace Bling {
  public class SmartString {
    public readonly Action<StringBuilder> A;
    public SmartString(Action<StringBuilder> A) { this.A = A; }
    public static implicit operator SmartString(string s) {
      return new SmartString((bld) => bld.Append(s));
    }
    public static SmartString operator +(SmartString opA, SmartString opB) {
      return new SmartString((bld) => { opA.A(bld); opB.A(bld); });
    }
    public static SmartString operator +(SmartString opA, string opB) {
      return new SmartString((bld) => { opA.A(bld); bld.Append(opB); });
    }
    public static SmartString operator +(string opA, SmartString opB) {
      return new SmartString((bld) => { bld.Append(opA); opB.A(bld); });
    }
    public override string ToString() {
      var bld = new StringBuilder();
      A(bld);
      return bld.ToString();
    }
    public override int GetHashCode() {
      throw new NotSupportedException();
    }
    public override bool Equals(object obj) {
      throw new NotSupportedException();
    }
  }
}

namespace Bling.Ops {
  using Shaders;


  public partial interface IOperatorX<S, T> : IOperator<T> {
    Func<SmartString, SmartString> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<S, T> op) where EVAL : Eval<EVAL>;
  }
  public partial interface OperatorX<S, T, U> : IOperator<U> {
    Func<SmartString, SmartString, SmartString> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<S, T, U> op) where EVAL : Eval<EVAL>;
  }
  public partial interface OperatorX<S, T, U, V> : IOperator<V> {
    Func<SmartString, SmartString, SmartString, SmartString> Shader(Operation<S, T, U, V> op);
  }
  public partial interface OperatorX<S, T, U, V, W> : IOperator<W> {
    Func<SmartString, SmartString, SmartString, SmartString, SmartString> Shader(Operation<S, T, U, V, W> op);
  }
  public partial class BaseOperatorX<S, T> : BaseOperatorX<T>, IOperatorX<S, T> {
    public virtual Func<SmartString, SmartString> Shader(Operation<S, T> op) { return null; }
    public virtual Func<SmartString, SmartString> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<S, T> op) where EVAL : Eval<EVAL> { return Shader(op); }
  }
  public partial class BaseOperatorX<S, T, U> {
    public virtual Func<SmartString, SmartString, SmartString> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<S, T, U> op) where EVAL : Eval<EVAL> { return null; }
  }
  public partial class BaseOperatorX<S, T, U, V> {
    public virtual Func<SmartString, SmartString, SmartString, SmartString> Shader(Operation<S, T, U, V> op) { return null; }
  }
  public partial class BaseOperatorX<S, T, U, V, W> {
    public virtual Func<SmartString, SmartString, SmartString, SmartString, SmartString> Shader(Operation<S, T, U, V, W> op) { return null; }
  }
  /* move to OldShaders
  public abstract partial class BinaryOperator<S, T, CNT> : AssociativeOperator<S, T, CNT> {
    public override Func<string, string, string> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<S, S, T> op) {
      return Format;
    }
  }
  public abstract partial class BaseStaticCallOperator<T, S, CNT> : Operator<T, S, CNT> {
    public virtual string ShaderCallName { get { return CallName.ToLower(); } }
    public override Func<string, string> Shader(Operation<T, S> op) {
      return (t) => ShaderCallName + "(" + t + ")";
    }
  }
  public abstract partial class StaticCallOperator<T, S, U, CNT> : Operator<T, S, U, CNT> {
    public virtual string ShaderCallName { get { return CallName.ToLower(); } }
    public override Func<string, string, string> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<T, S, U> op) {
      return (t, s) => ShaderCallName + "(" + t + ", " + s + ")";
    }
  }
  public abstract partial class StaticCallOperator<T, S, U, V, CNT> : Operator<T, S, U, V, CNT> {
    public virtual string ShaderCallName { get { return CallName.ToLower(); } }
    public override Func<string, string, string, string> Shader(Operation<T, S, U, V> op) {
      return (t, s, u) => ShaderCallName + "(" + t + ", " + s + ", " + u + ")";
    } 
  }
  public abstract partial class UniformUnaryOperator<T, K, CNT> : Ops.Operator<T, T, CNT> {
    public override Func<string, string> Shader(Operation<T, T> op) { return Format; }
  }
   */
  public partial class Operator<S, T, U, V, CNT> : BaseOperatorX<S, T, U, V>, IOperator<S, T, U, V, CNT> where CNT : Operator<S,T,U,V,CNT> {
    public override Func<SmartString, SmartString, SmartString, SmartString> Shader(Operation<S, T, U, V> op) { return Format; }
  }
  public abstract partial class CastOperator<S, T, CNT> : Operator<S, T, CNT> {
    public override Func<SmartString, SmartString> Shader<EVAL>(BaseShaderEval<EVAL> txt, Operation<S, T> op) {
      return s => ("(" + txt.TypeNameFor(typeof(T)) + ") ") + s;
    }

  }
}
namespace Bling.Shaders {
  using Ops = Bling.Ops;
  using Bling.Vecs;
  using Bling.Ops;
  using Brands = Bling.Core;
  using Bling.Core;

  public partial class SampleException : Exception {
    public SampleException(string msg) : base(msg) { }
  }
  

  public delegate Func<Double2Bl,ColorBl> CustomTexture(Tex2D Original);

  public abstract partial class BaseShaderEval<EVAL> : ScopedEval<EVAL>, Ops.IConditionEval2<EVAL>, ITableEval<EVAL>, ISampleEval<EVAL>, IVecEval<EVAL>, IMatrixEval<EVAL>, WPF.IWPFEval<EVAL> where EVAL : Eval<EVAL> {




    public abstract Info<IVec<double, D4>> Sample<DIM, PT>(Expr<Texture<DIM>> argA, Expr<PT> argB) where DIM : Dim1D<DIM>;
    protected override Info<S> AllocateTemp<S>(Scope scope, Info<S> Code) {
      return NewInfo<S>(TempName, Code); // copy attributes from code. 
    }
    public override Func<Eval<EVAL>.Info<S>, Eval<EVAL>.Info<T>> Operator<S, T>(Bling.Ops.Operation<S, T> op) {
      var shader = op.Op.Shader(this, op);
      if (shader == null) return base.Operator<S, T>(op);
      return (s) => Append(() => NewInfo<T>(shader(ValueFor(s))), new Expr[] {
          op.ArgA, 
        }, s);
    }
    public override Func<Eval<EVAL>.Info<S>, Eval<EVAL>.Info<T>, Eval<EVAL>.Info<U>> Operator<S, T, U>(Bling.Ops.Operation<S, T, U> op) {
      var shader = op.Op.Shader(this, op);
      if (shader == null) return base.Operator<S, T, U>(op);
      return (s, t) => Append(() => NewInfo<U>(shader(ValueFor(s), ValueFor(t))), new Expr[] {
          op.ArgA, op.ArgB, 
        }, s, t);
    }
    public override Func<Eval<EVAL>.Info<S>, Eval<EVAL>.Info<T>, Eval<EVAL>.Info<U>, Eval<EVAL>.Info<V>> Operator<S, T, U, V>(Bling.Ops.Operation<S, T, U, V> op) {
      var shader = op.Op.Shader(op);
      if (shader == null) return base.Operator<S, T, U, V>(op);
      return (s, t, u) => Append(() => NewInfo<V>(shader(ValueFor(s), ValueFor(t), ValueFor(u))),
        new Expr[] {
          op.ArgA, op.ArgB, op.ArgC, 
        }, s,t,u);
    }
    public override Func<Eval<EVAL>.Info<S>, Eval<EVAL>.Info<T>, Eval<EVAL>.Info<U>, Eval<EVAL>.Info<V>, Eval<EVAL>.Info<W>> Operator<S, T, U, V, W>(Bling.Ops.Operation<S, T, U, V, W> op) {
      var shader = op.Op.Shader(op);
      if (shader == null) return base.Operator<S, T, U, V, W>(op);
      return (s, t, u, v) => Append(() => NewInfo<W>(shader(ValueFor(s), ValueFor(t), ValueFor(u), ValueFor(v))),
        new Expr[] {
          op.ArgA, op.ArgB, op.ArgC, op.ArgD, 
        },
        s,t,u,v);
    }
    public void Composite0(MyInfo InfoT, params Info[] infos) {
      SmartString str = TypeNameFor(InfoT.TypeOfT) + "(";
      for (int i = 0; i < infos.Length; i++) {
        str = str + (i == 0 ? "" : ", ") + ((MyInfo)infos[i]).Value;
      }
      InfoT.Value = str + ")";
    }
    private int ConditionCounter = 0;
    public virtual MyInfo<T> Block<T>(string cname, Expr<T> Expr) {
      PushScope();
      var exprI = DoEval1(Expr);
      if (exprI.Value == null) {
        PopScope();
        return exprI;
      }
      return Block(cname, exprI);
    }

    protected virtual MyInfo<T> Block<T>(string cname, MyInfo<T> exprI) {
      (exprI.Value != null).Assert();
      // don't use append here, already buit into exprI.
      var temps = PopScope();
      var ret = NewInfo<T>(cname, exprI); // because we will copy this expression's shader mode.
      ret.Append("{");
      // dump temps.
      foreach (var t in temps) {
        (((MyInfo)t.AsTemp).BlockBefore == null).Assert();
        ret.Append(t.Code);
        ret.Append("  " + this.TypeNameFor(((MyInfo)t.AsTemp).TypeOfT) + " " + ((MyInfo)t.AsTemp).Value + " = " + ((MyInfo)t.Code).Value + ";");
      }
      ret.Append(exprI); // for any of exprI's baggage
      ret.Append("  " + cname + " = " + exprI.Value + ";", "}");
      return ret;
    }
    private class Block0Visitor : MyInfoVisitor<MyInfo> {
      public BaseShaderEval<EVAL> Outer; public string cname;
      public MyInfo Visit<T>(MyInfo<T> Info) {
        return (Outer).Block<T>(cname, Info);
      }
    }
    protected MyInfo Block(string cname, MyInfo exprI) {
      return exprI.Accept(new Block0Visitor() { Outer = this, cname = cname });
    }


    public void Flatten(string Indent0, Info Info, System.IO.StringWriter Output) {
      var Info0 = (MyInfo)Info;
      if (Info0.BlockBefore == null) return;
      // e = string | list of e
      string Indent = Indent0;
      Action<Action> DoIndent = (a) => {
        var oldIndent = Indent;
        Indent += "  ";
        a();
        Indent = oldIndent;
      };
      Action<List<object>> Process = null;
      Process = list => {
        (list.Count > 0).Assert();
        foreach (var e in list) {
          if (e is string) Output.WriteLine(Indent + ((string)e));
          else if (e is SmartString) {
            Output.WriteLine((Indent + ((SmartString)e)).ToString());
          } else DoIndent(() => Process((List<object>)e));
        }
      };
      Process(Info0.BlockBefore);
    }

    public virtual Eval<EVAL>.Info<T> Block<T>(T[] Values, Eval<EVAL>.Info<int> idx) { throw new NotSupportedException(); }
    public virtual Eval<EVAL>.Info<T> Table<T>(T[] Cases, Expr<int> Index) {
      var Idx = DoEval1(Index);
      if (Idx.Value == null) 
        return NewInfo<T>(null, Idx);
      var cname = "test" + ConditionCounter;
      ConditionCounter += 1;
      var ret = Prepare<T>(() => NewInfo<T>(cname), new Expr[] { Index }, Idx);
      if (ret.Value != null) {
        ret.Append(TypeNameFor(typeof(T)) + " " + cname + ";");
        ret.Append("switch (" + Idx.Value + ") {");
        int i = 0;
        foreach (var Case in Cases) {
          ret.Append("  case " + i + ": " + cname + " = " + DoEval1(new Constant<T>(Cases[i])).Value + "; break;");
          i++;
        }
        ret.Append("  default: " + cname + " = " + DoEval1<T>(new Constant<T>(VecUtils.DefaultFor<T>())).Value + "; break;");
        ret.Append("}");
      }
      return ret;
    }

    public virtual Eval<EVAL>.Info<T> Table<T>(Expr<T>[] Values, Expr<int> Index) {
      var cname = "test" + ConditionCounter;
      ConditionCounter += 1;
      var Idx = DoEval1(Index);
      if (Idx.Value == null)
        return NewInfo<T>(null, Idx);

      MyInfo<T>[] Cases = new MyInfo<T>[Values.Length];
      Expr[] All = new Expr[Values.Length + 1];
      MyInfo[] Infos = new MyInfo[Values.Length + 1];
      All[0] = Index;
      Infos[0] = Idx;
      for (int i = 0; i < Cases.Length; i++) {
        PushScope();
        Cases[i] = Block(cname, DoEval1(Values[i]));
        Infos[i + 1] = Cases[i];
        //(Cases[i].Value == cname || Cases[i].Value == null).Assert();
        All[i + 1] = Values[i];
      }
      var ret = Prepare<T>(() => NewInfo<T>(cname), All, Infos);
      if (ret.Value != null) {
        ret.Append(TypeNameFor(typeof(T)) + " " + cname + ";");
        ret.Append("switch (" + Idx.Value + ") {");
        int i = 0;
        foreach (var Case in Cases) {
          ret.Append("  case " + i + ": ");
          if (Case.BlockBefore != null) //(Case.Value == cname)
            ret.Append(Case);
          else {
            (Case.BlockBefore == null && Case.Value != null).Assert();
            ret.Append("  " + cname + " = " + Case.Value + ";");
          }
          ret.Append("  break;");
          i++;
        }
        ret.Append("  default: " + cname + " = " + DoEval1<T>(new Constant<T>(VecUtils.DefaultFor<T>())).Value + "; break;");
        ret.Append("}");
      }
      return ret;
    }
    public override Eval<EVAL>.Info<S> Memo<S>(IExpr op, Func<Eval<EVAL>.Info<S>> Code, params Eval<EVAL>.Info[] args) {
      var ret = (MyInfo<S>)base.Memo<S>(op, Code, args);
      /*
      if (args.Length > 0 && ((MyInfo)args[0]).BlockBefore != null) {
        if (ret.BlockBefore == null) {
          var ret0 = (MyInfo<S>)base.Memo<S>(op, Code, args);
          false.Assert();
        }
      }*/
      //ret.Append(args);
      return ret;
    }
    public Eval<EVAL>.Info<S> Condition<S>(Expr<bool> Test, Expr<S> IfTrue, Expr<S> IfFalse) {
        var Te = DoEval1(Test);
        var IfT = DoEval1(IfTrue);
        var IfF = DoEval1(IfFalse);
        return Prepare<S>(() => NewInfo<S>("(" + Te.Value + " ? " + IfT.Value + " : " + IfF.Value + ")"),
          new Expr[] { Test, IfTrue, IfFalse, }, Te, IfT, IfF);
    }
    public Eval<EVAL>.Info<S> DCondition<S>(Expr<bool> Test, Expr<S> IfTrue, Expr<S> IfFalse) {
      var TestE = DoEval1(Test);
      var cname = "test" + ConditionCounter;
      ConditionCounter += 1;
      var IfTrueE = Block(cname, IfTrue);
      var IfFalseE = Block(cname, IfFalse);
      // we only care about the block before.
      //(IfTrueE.Value == cname || IfTrueE.Value == null).Assert();
      //(IfFalseE.Value == cname || IfFalseE.Value == null).Assert();

      var ret = Prepare<S>(() => NewInfo<S>(cname), new Expr[] { Test, IfTrue, IfFalse }, TestE, IfTrueE, IfFalseE);

      // custom append
      if (ret.Value != null) {
        ret.Append(TypeNameFor(typeof(S)) + " " + cname + ";");
        ret.Append("if (" + TestE.Value + ") ");
        if (IfTrueE.BlockBefore != null)
          ret.Append(IfTrueE);
        else 
        {
          (IfTrueE.BlockBefore == null && IfTrueE.Value != null).Assert();
          ret.Append("  " + cname + " = " + IfTrueE.Value + ";");
        }

        ret.Append("else");
        if (IfFalseE.BlockBefore != null)
          ret.Append(IfFalseE);
        else 
        {
          (IfFalseE.BlockBefore == null && IfFalseE.Value != null).Assert();
          ret.Append("  " + cname + " = " + IfFalseE.Value + ";");
        }
      } else (ret.BlockBefore == null).Assert();
      return ret;
    }
    protected virtual bool SupportsOthers { get { return false; } }
    public interface MyInfoVisitor<R> {
        R Visit<T>(MyInfo<T> Info);
      }

    public MyInfo DoEval1(Expr Expr) { return (MyInfo)((Eval<EVAL>)this).DoEval(Expr); }
    public interface MyInfo : Info {
      List<object> BlockBefore { get; }
      SmartString Value { get; set; }
      Type TypeOfT { get; }
      R Accept<R>(MyInfoVisitor<R> Visitor);
      MyInfo BaseAppend(params Info[] Infos);
    }
    public abstract class RegisterFile {
      public readonly EVAL Outer;
      public RegisterFile(EVAL Outer) { this.Outer = Outer; }
      public readonly Dictionary<Expr, int> Registers = new Dictionary<Expr, int>();
      private readonly Dictionary<Expr, MyInfo> RegisterAllocs = new Dictionary<Expr, MyInfo>();
      public MyInfo RegisterAlloc(Expr e) { return RegisterAllocs[e]; }
      public MyInfo<T> RegisterAlloc<T>(Expr<T> e) { return (MyInfo<T>) RegisterAllocs[e]; }
      public virtual Expr[] FlushRegisters {
        get {
          var ret = new Expr[Registers.Count];
          foreach (var v in Registers) ret[v.Value] = v.Key;
          return ret;
        }
      }
      private class ExprVisitor : ExprVisitor<int> {
        public RegisterFile Outer;
        public MyInfo Info;
        public int Visit<T>(Expr<T> Expr) {
          var Info = (MyInfo<T>)this.Info;
          Outer.BringIn<T>(Expr, Info);
          return 0;
        }
      }
      protected virtual string RegisterName<T>(Expr<T> Expr, int Idx, MyInfo<T> Info) { return "r" + Idx; }
      protected abstract bool BringIn0<T>(MyInfo<T> Info);
      protected virtual void Copy<T>(MyInfo<T> From, MyInfo<T> To) {
        To.Value = From.Value;
      }

      public virtual void BringIn<T>(Expr<T> Expr, MyInfo<T> Info) {
        if (Info.Value != null) return;
        if (!BringIn0(Info)) return;
        if (Registers.ContainsKey(Expr)) {
          Copy(RegisterAlloc(Expr), Info);
        } else {
          RegisterAllocs[Expr] = BringInNew(Expr, Info);
        }
        return;
      }
      public virtual MyInfo<T> BringInNew<T>(Expr<T> Expr, MyInfo<T> Info) {
        var name = RegisterName(Expr, Registers.Count, Info);
        Info.Value = name;
        Registers[Expr] = Registers.Count;
        return Info;
      }

      public void BringIn(Expr e, MyInfo info) {
        e.Visit((new ExprVisitor() { Outer = this, Info = info }));
      }
    }
    protected virtual MyInfo<T> Prepare<T>(Func<MyInfo<T>> Target, Expr[] Exprs, params Info[] Infos) {
      var AllNotNull = true;
      foreach (var info in Infos) AllNotNull = AllNotNull && ((MyInfo)info).Value != null;
      if (AllNotNull)
        return Target();
      else return NewInfo<T>(null);
    }
    public virtual MyInfo<T> Append<T>(Func<MyInfo<T>> Target, Expr[] Exprs, params Info[] Infos) {
      return (MyInfo<T>) Prepare(Target, Exprs, Infos).BaseAppend(Infos);
    }
    public class MyInfo<T> : Info<T>, MyInfo {
      public List<object> BlockBefore { get; set; }
      public SmartString Value { get; set; }
      public Type TypeOfT { get { return typeof(T); } }

      public MyInfo BaseAppend(params Info[] infos) { return Append(infos); }

      public MyInfo<T> Append(params Info[] infos) {
        foreach (var info in infos) {
          var info0 = (MyInfo)info;
          if (info0.BlockBefore == null) continue;
          if (BlockBefore == null) BlockBefore = new List<object>();
          (info0.BlockBefore.Count > 0).Assert();
          if (info0.BlockBefore.Count == 1) BlockBefore.Add(info0.BlockBefore[0]);
          else BlockBefore.Add(info0.BlockBefore);
        }
        return this;
      }
      public R Accept<R>(MyInfoVisitor<R> Visitor) {
        return Visitor.Visit(this);
      }
      public MyInfo<T> Append(params SmartString[] stuff) {
        if (BlockBefore == null) {
          BlockBefore = new List<object>();
        }
        foreach (var s in stuff) BlockBefore.Add(s);
        return this;
      }
    }
    public MyInfo<T> DoEval1<T>(Expr<T> Expr) { return (MyInfo<T>)((Eval<EVAL>)this).DoEval(Expr); }
    public abstract MyInfo<T> NewInfo<T>(SmartString Value);
    protected SmartString ValueFor<T>(Info<T> Info) { 
      var ret = ((MyInfo<T>)Info).Value;
      (ret != null).Assert();

      return ret;
    }
    public abstract MyInfo<T> NewInfo<T>(SmartString Value, Info Copy);
  }
  public abstract partial class ShaderEval<EVAL> : BaseShaderEval<EVAL>, Ops.IConditionEval<EVAL> /* , TableEval<EVAL> */ where EVAL : Eval<EVAL> {
    public override BaseShaderEval<EVAL>.MyInfo<T> NewInfo<T>(SmartString Value) { return new MyInfo<T>() { Value = Value, InShaderStatus = false }; }
    public override BaseShaderEval<EVAL>.MyInfo<T> NewInfo<T>(SmartString Value, Info Copy) { return new MyInfo<T>() { Value = Value, InShaderStatus = ((MyInfo)Copy).InShaderStatus }; }

    protected override Info<S> AllocateTemp<S>(Scope scope, Info<S> Code) {
      var Code0 = (MyInfo<S>)Code;
      if (!Code0.InShader) return null;
      (Code0.InShader).Assert(); // otherwise, we shouldn't be allocating a temp!
      return base.AllocateTemp<S>(scope, Code);
    }

    protected override BaseShaderEval<EVAL>.MyInfo<T> Prepare<T>(Func<BaseShaderEval<EVAL>.MyInfo<T>> Target, Expr[] Exprs, params Eval<EVAL>.Info[] Infos) {
      ShaderStatus InShaderStatus = new OutShader();
      var IsConstant = true;
      foreach (var info in Infos) {
        InShaderStatus = InShaderStatus | ((MyInfo)info).InShaderStatus;
        IsConstant = IsConstant && ((MyInfo)info).Value != null;
      }
      IsConstant = IsConstant && !InShaderStatus.In;

      if (!InShaderStatus.In && !IsConstant) { // get rid of any constant values. 
        foreach (var info in Infos) ((MyInfo)info).Value = null;

        var ret0 = base.Prepare(Target, Exprs, Infos);
        ((MyInfo)ret0).Value = null;
        ((MyInfo)ret0).InShaderStatus = InShaderStatus;
        return ret0;
      }
      for (int i = 0; i < Infos.Length; i++) {
        if (InShaderStatus.In && !((MyInfo) Infos[i]).InShader && ((MyInfo) Infos[i]).Value == null) 
          Registers.BringIn(Exprs[i], (MyInfo)Infos[i]);
      }
      var ret = base.Prepare<T>(Target, Exprs, Infos);
      ((MyInfo) ret).InShaderStatus = InShaderStatus;
      if (InShaderStatus.In) (ret.Value != null).Assert();
      return ret;
    }
    protected virtual ShaderRegisterFile MakeRegisterFile() { return new ShaderRegisterFile(this); }
    public ShaderEval() {
      Registers = MakeRegisterFile();
    }
    public class ShaderRegisterFile : RegisterFile {
      public ShaderRegisterFile(EVAL Outer) : base(Outer) { }
      protected override bool BringIn0<T>(BaseShaderEval<EVAL>.MyInfo<T> Info) {
        var Info0 = (MyInfo<T>)Info;
        (!Info0.InShader).Assert();
        (Info0.BlockBefore == null).Assert();
        Info0.InShaderStatus = true;
        return true;
      }
      protected override void Copy<T>(BaseShaderEval<EVAL>.MyInfo<T> From, BaseShaderEval<EVAL>.MyInfo<T> To) {
        base.Copy<T>(From, To);
        ((MyInfo<T>)To).InShaderStatus = ((MyInfo<T>)From).InShaderStatus;
      }
    }
    public readonly ShaderRegisterFile Registers;
    public abstract class ShaderStatus {
      public abstract bool In { get; }
      public static implicit operator ShaderStatus(bool b) {
        return (b) ? (ShaderStatus) new InShader() : new OutShader();
      }
      public static ShaderStatus operator |(ShaderStatus opA, ShaderStatus opB) {
        if (opA is InShader || opB is InShader) return new InShader();
        return ((OutShader)opA).Merge((OutShader)opB);
      }
    }
    public sealed class InShader : ShaderStatus {
      public override bool In {
        get { return true; }
      }
      public override string ToString() {
        return "In";
      }
    }
    public class OutShader : ShaderStatus {
      public int Depth = 0;
      public override bool In {
        get { return false; }
      }
      public override string ToString() {
        return "Out-" + Depth;
      }
      public OutShader Merge(OutShader o1) {
        return new OutShader() { Depth = Math.Max(Depth, o1.Depth) };
      }
    }

    public new interface MyInfo : BaseShaderEval<EVAL>.MyInfo {
      ShaderStatus InShaderStatus { get; set; }
      bool InShader { get; } // set;  }
    }
    public new MyInfo<T> DoEval<T>(Expr<T> Expr) { return (MyInfo<T>)((Eval<EVAL>)this).DoEval(Expr); }
    public new class MyInfo<T> : BaseShaderEval<EVAL>.MyInfo<T>, MyInfo {
      public ShaderStatus InShaderStatus { get; set; }
      public bool InShader {
        get { return InShaderStatus.In; }
        // set { InShaderStatus = (value ? (ShaderStatus) new InShader() : new OutShader()); }
      }
      public MyInfo() { // false.
        InShaderStatus = new OutShader();
      }
    }
  }
}
/*
namespace Bling {
  using Bling.Vecs;
  using Bling.Core;
  // points with configurable roles?

  namespace Semantics {
    public abstract class PointRole<ROLE> : Role<ROLE> where ROLE : PointRole<ROLE>, new() { }
    public class Normal : PointRole<Normal> { }
    public class Position : PointRole<Position> { }
    public class TexCoord : PointRole<TexCoord> { }

    public class Color : Role<Color> { }
  }
}
*/
